type Event = { callback: Function; caller: any; once: boolean };

class EventManager {
  private static instance: EventManager;
  public static get Ins() {
    if (!this.instance) this.instance = new EventManager();
    return this.instance;
  }
  private constructor() {}
  public static clear() {
    this.instance = null;
  }
  private eventDic: { [eventName: string]: Event[] } = {};
  public on(eventName: string, callback: Function, caller: any) {
    let eventList = this.eventDic[eventName];
    if (!eventList) {
      eventList = [];
      this.eventDic[eventName] = eventList;
    }
    for (const event of eventList) {
      if (event.callback == callback && event.caller == caller) {
        console.log('event:' + eventName + '---isOn');
        return;
      }
    }
    eventList.push({ callback: callback, caller: caller, once: false });
  }

  public once(eventName: string, callback: Function, caller: any) {
    let eventList = this.eventDic[eventName];
    if (!eventList) {
      eventList = [];
      this.eventDic[eventName] = eventList;
    }
    for (const event of eventList) {
      if (event.callback == callback && event.caller == caller) {
        console.log('event:' + eventName + '---isOn');
        return;
      }
    }
    eventList.push({ callback: callback, caller: caller, once: true });
  }

  public off(eventName: string, callback: Function, caller: any) {
    let eventList = this.eventDic[eventName];
    if (!eventList) return;

    eventList = eventList.filter((event) => {
      return event.callback != callback || event.caller != caller;
    });
    this.eventDic[eventName] = eventList;
  }

  public offCaller(caller: any) {
    for (const eventName in this.eventDic) {
      if (Object.prototype.hasOwnProperty.call(this.eventDic, eventName)) {
        let eventList = this.eventDic[eventName];
        eventList = eventList.filter((event) => {
          return event.caller != caller;
        });
        this.eventDic[eventName] = eventList;
      }
    }
  }

  public emit(eventName: string, ...args) {
    let eventList = this.eventDic[eventName];
    if (!eventList) return;
    eventList = eventList.filter((event) => {
      event.callback.apply(event.caller, args);
      return !event.once;
    });
    this.eventDic[eventName] = eventList;
  }

  public clear() {
    this.eventDic = {};
  }
}

export default EventManager;
